\chapter{表达式}

表达式是代表了一个可以求值的由至少一个\verb!操作数、0或多个!\verb!操作符!组成的序列。
操作数通常是有类型的对象，例如字面量、变量或者有返回值的函数调用等。
操作符是人为规定的符号，代表了要对其操作数所要进行的操作。
例如，在表达式 a + b中，a和b是操作数，\textquotesingle +\textquotesingle 是操作符。
\textquotesingle +\textquotesingle 通常被规定为对两个数进行求和。

表达式也有类型和值，其类型和值由其操作符和操作数决定。

在Tenon语言中，有些操作符有1个操作数，例如自增运算符++；
有些操作符有2两个操作数，例如加法运算符+。
有返回值的函数调用可以认为是特殊的操作符，其可以有0或多个操作数。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    expr = literal | variable | binary_expr | unary_expr
           | func_call | aggregate_expr | '(' expr ')'

    expr_list = expr | expr_list ',' expr
\end{verbatim}

Tenon的表达式包括字面量表达式、变量表达式、二元操作符表达式、一元操作符表达式、
函数调用表达式、聚合表达式等。每个表达式都有\verb!类型!和\verb!值!的概念。
一个被括号包含的表达式也是表达式，其类型和值与括号内的表达式的类型和值相同。

一个或多个表达式可以构成\verb!表达式列表!。

\section{字面量表达式}

\textbf{语法}
\begin{verbatim}
    literal = BOOLEAN_LITERAL | NUMBER_LITERAL | STRING_LITERAL | enum_const
    enum_const = IDENTIFIER DOUBLE_COLON IDENTIFIER
\end{verbatim}

一个字面量（包括布尔型字面量、数值字面量、字符串字面量、枚举值字面量）是一个表达式。
字面量的数据类型和值即字面量代表的表达式的类型和值。
例如，字面量 true 是一个表达式，其类型为布尔型，其值为真；
字面量 3.14 是一个表达式，其类型为数值型，其值为实数3.14；
字面量\textquotedbl hello world\textquotedbl 是一个表达式，
其类型为字符串型，其值为字符串\textquotedbl hello world\textquotedbl；
字面量Color::Black是一个表达式，其类型为Color，其值为Color::Black。

\section{变量表达式}

变量表达式可以分为\verb!普通变量表达式!、\verb!元素变量表达式!、\verb!成员变量表达式!。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    variable = entire
               | entire dim_expr_list
               | variable '.' IDENTIFIER
    entire = IDENTIFIER
\end{verbatim}

\subsection{普通变量表达式}

一个已经声明为变量的标识符是一个表达式。
变量的类型和值即标识符代表的表达式的类型和值。例如：

\begin{verbatim}
    num n1 = 10.0;
    bool b1 = true;
\end{verbatim}

在上面的示例中，声明了两个变量n1和b1，并分别被赋值为10.0和true。
在此之后，表达式n1的类型为数值型，值为10.0；
表达式b1的类型为布尔型，值为true。若n1和b1的类型和值发生了变化，
则其代表的表达式的类型和值也发生相应的变化。

没有经过声明的标识符不是表达式，会引起语法错误。

\subsection{元素变量表达式}

元素变量表达式可以用来获取数组变量的元素的值，
数组变量由表达式entire给出，
元素的索引由括号内的表达式列表dim\_expr\_list给出。
需要注意的是，Tenon中的数组最多为3维，因此dim\_expr\_list中需要包含1至3个表达式；
Tenon中的数组索引从1开始，因此每个表达式的值必须为大于0的整数值。

元素变量表达式的类型和数组元素的类型相同，且其值等于由索引得到的数组元素的值。
例如：

\begin{verbatim}
    num n1;
    num narr[2][3] = {{1, 2, 3}, {4, 5, 6}};
    n1 = narr[1][2];    // n1为2
\end{verbatim}

在上面的示例中，声明了一个二维数组narr，元素变量表达式narr[1][2]可以用来获取数组narr第一维的索引为1、
第二维的索引为2的数组元素的值，也就是2。

\subsection{成员变量表达式}

成员变量表达式可以用来获取记录类型变量的成员的值。记录类型变量由表达式variable给出，
成员由词法元素IDENTIFIER给出。
记录类型变量的成员的类型和值也是成员变量表达式的类型和值。
例如：

\begin{verbatim}
    num n1;
    pos p1 = {1, 2, 3};
    n1 = p1.x; // 获取记录类型变量p1的成员x的值

    num n2;
    pos parr[2] = {{1, 2, 3}, {4, 5, 6}};
    n2 = parr[2].y; // 获取记录类型变量parr[2]的成员y的值
\end{verbatim}

在上面的示例中，记录类型变量分别为普通变量p1和数组元素变量parr[2]。

\subsection{按值传参和按引用传参}

通常情况下，变量表达式的值是一个值，比如数值2、布尔值false、pos类型
值{1, 2, 3}等等，除此之外，在作为函数的引用参数时，变量表达式代表的是
该\verb!变量的引用!。函数内对引用参数的修改相当于修改作为参数的变量本
身。

\section{二元操作符表达式}

二元操作符包括四则运算符、关系运算符、逻辑运算符等，
由这些运算符和两个操作数构成的序列即二元操作符表达式。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    binary_expr = expr '+' expr
                  | expr '-' expr
                  | expr '*' expr
                  | expr '/' expr
                  | expr EQ expr
                  | expr NE expr
                  | expr LT expr
                  | expr LE expr
                  | expr GT expr
                  | expr GE expr
                  | expr OR expr
                  | expr AND expr
\end{verbatim}

四则运算符包括+、-、*、/，分别代表普通的加、减、乘、除四则运算。
除自定义记录类型外，通常四则运算符只能用于数值类型，
但有一个例外是加法运算符+可以用于字符串类型。
四则运算表达式的类型和其操作数的类型一致。例如，数值类型的四则运算表达式的类型为数值类型；
字符串类型的加法运算表达式的类型为字符串类型。

关系运算符包括==、!=、<、<=、>、>=，一一对应于语法分析时的记号EQ、NE、LT、LE、GT、GE，
分别表示关系运算中的相等、不相等、小于、小于等于、大于、大于等于。
等于/不等于运算表达式可以用于几乎所有类型，
其它关系运算表达式通常用于数值类型。
所有关系运算表达式的类型为布尔型，表达式的值由表达式的比较结果决定。
需要注意的是，对于数值类型，无法精确判断两个操作数是否相等。

逻辑运算符包括or、and，分别对应于语法分析时的记号OR、AND，
分别代表逻辑运算中的或、与。
逻辑运算的两个操作数都必须是布尔类型，结果也是布尔类型。

%TO DO: 与、或表达式的省略求值情形

除自定义类型外，二元操作符表达式的类型和值可以用下表汇总给出：

\vspace{2ex}

\noindent\begin{tabular}{|>{\centering\arraybackslash}p{3em}|>{\centering\arraybackslash}p{5em}|>{\centering\arraybackslash}p{5em}|>{\centering\arraybackslash}p{15em}|}
\hline
 操作符 & 操作数类型 & 表达式类型 & 表达式的值\\
\hline
 + & 数值 & 数值 & 两个操作数的和\\
 + & 字符串 & 字符串 & 两个操作数的拼接\\
 - & 数值 & 数值 & 两个操作数的差\\
 {*} & 数值 & 数值 & 两个操作数的积\\
 / & 数值 & 数值 & 两个操作数的商\\
 == & 任意 & 布尔 & 两个操作数是否相等 \\
 != & 任意 & 布尔 & 两个操作数是否不等 \\
 < & 数值 & 布尔 & 左操作数是否小于右操作数 \\
 <= & 数值 & 布尔 & 左操作数是否小于等于右操作数 \\
 > & 数值 & 布尔 & 左操作数是否大于右操作数 \\
 >= & 数值 & 布尔 & 左操作数是否大于等于右操作数 \\
 or & 布尔 & 布尔 & 左操作数与右操作数的逻辑或值 \\
 and & 布尔 & 布尔 & 左操作数与右操作数的逻辑与值 \\
\hline
\end{tabular}

\vspace{2ex}

Tenon中的运算符都是左结合的，且优先级等级如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{14em}|}
\hline
优先级 & 运算符\\
\hline
 1 & * / \\
 2 & + - \\
 3 & < <= > >= \\
 4 & == != \\
 5 & and \\
 6 & or \\
\hline
\end{tabular}

\vspace{2ex}

在进行表达式求值时，会优先计算括号内包含的表达式，因此括号可以用来改变表达式的求值顺序。
虽然规定了上表的表达式优先级，但通常情况下，使用括号来明确表示计算顺序会使表达式有更高的可读性。
在代码中不应当出现 a+++b 这样可读性非常差的代码。

\vspace{2ex}

\section{一元操作符表达式}

一元运算符包括逻辑非、正负号、自增、自减等，由这些运算符和一个操作数构成的序列即一元操作符表达式。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    unary_expr = NOT expr
                 | '+' expr
                 | '-' expr
                 | PLUSPLUS variable
                 | SUBSUB variable
                 | variable PLUSPLUS
                 | variable SUBSUB
\end{verbatim}

逻辑非运算符由符号!表示，对应语法分析时的记号NOT，代表逻辑运算中的非运算。
（除自定义类型外）只有布尔类型的表达式可以进行逻辑非运算。

正负号运算符包括+、-，分别表示求正运算和求负运算。
（除自定义类型外）只有数值类型的表达式有求正运算和求负运算，
并且通常求正运算并无实际意义。

自增运算符即++，对应语法分析时的符号PLUSPLUS，代表对表达式进行自增操作；
自减运算符即- -，对应语法分析时的符号SUBSUB，代表对表达式进行自减操作。
（除自定义类型外）只有数值类型的变量有自增运算和自减运算。
对同一变量，前缀形式的自增/自减表达式的值和后缀形式的自增/自减表达式的值不同。
前缀形式有``对变量先自增（自减）后取值"的含义；
后缀形式有``对变量先取值后自增（自减）"的含义。例如：

\begin{verbatim}
    n1 = 10.0;
    n2 = ++n1; // "先自增后取值", n1为11，n2也为11
    n3 = n2++; // "先取值后自增", n3为11，n2为12
    n4 = --n1; // "先自减后取值", n1为10，n4也为10
    n5 = n4--; // "先取值后自减", n5为10，n4为9
\end{verbatim}

除自定义类型外，一元操作符表达式的类型和值可以用下表汇总给出：

\noindent\begin{tabular}{|>{\centering\arraybackslash}p{8em}|>{\centering\arraybackslash}p{5em}|>{\centering\arraybackslash}p{5em}|>{\centering\arraybackslash}p{8em}|}
\hline
 操作符 & 操作数类型 & 表达式类型 & 表达式的值\\
\hline
 ! & 布尔 & 布尔 & 操作数的逻辑非值\\
 + & 数值 & 数值 & 操作数的值\\
 - & 数值 & 数值 & 操作数的负值\\
 ++（前缀） & 数值 & 数值 & 操作数的值加1\\
 --（前缀） & 数值 & 数值 & 操作数的值减1\\
 ++（后缀） & 数值 & 数值 & 操作数的值\\
 --（后缀） & 数值 & 数值 & 操作数的值\\
\hline
\end{tabular}

\section{函数调用表达式}

函数调用表达式以给定的函数参数执行被调函数的函数体，获取函数返回值。
函数返回值的类型和值即函数调用表达式的类型和值。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    func_call = IDENTIFIER '(' arg_list_opt ')'
                | variable '.' IDENTIFIER '(' arg_list_opt ')'

    arg_list_opt = empty | arg_list

    arg_list = func_arg | arg_list ',' func_arg

    func_arg = expr | IDENTIFIER '=' expr
\end{verbatim}

函数调用表达式包含普通函数调用表达式、记录类型成员函数调用表达式。
相同的是，两种函数调用表达式都包含了可选的参数列表；
不同的是，其给出被调用函数函数名的方式不同。

函数调用表达式中的被调用函数可以先使用后定义。可以直接调用定义在其他模块中的函数，无需前置声明。

\subsection{普通函数调用表达式}

普通函数调用表达式通过标识符给出被调用函数的函数名，可以用于两种情形：
一种是调用非记录类型成员函数，一种是调用同一记录类型中的成员函数。
例如：

\begin{verbatim}
    sum = add(2, 3);
\end{verbatim}

在上面的示例中，add(2, 3)是函数调用表达式，通过给出标识符add的方式给出函数名。

再比如：

\begin{verbatim}
    record arr {
        num x;
        num y;
        num z;

        num length() {
            return sqrt(x * x + y * y + z * z);
        }

        void normalize() {
            num len;
            len = length();
            x = x / len;
            y = y / len;
            z = z / len;
        }
    }
\end{verbatim}

在上面的示例中，normalize函数中调用了同一记录类型的length函数，
通过给出标识符length的方式。

\subsection{记录类型成员函数调用表达式}

如果记录类型外的函数中需要调用记录类型的成员函数，则需要通过该记录类型的实例进行。
例如：

\begin{verbatim}
    arr arr1 = {1, 2, 3};
    num arr1_len;
    arr1_len = arr1.length();
\end{verbatim}

在上面的示例中，通过变量arr1和标识符length的方式调用了arr类型的成员函数length。
请注意区别此处对length的调用与上面的示例中normalize函数对length的调用的区别。

在记录类型成员函数调用表达式中，变量必须为已经声明的变量。

\subsection{参数列表}

在函数调用表达式中，包含一个可选的参数列表，其中可以包含0或多个参数。
在实际中，包含的参数个数取决于被调用函数。

对于被调用函数的必需参数，在函数调用表达式中可以直接在对应的参数位置给出参数表达式；
对于可选参数，则需要同时给出参数的名称。例如：

\begin{verbatim}
    num func(num x, num y = 10.0) {
        return x * y;
    }

    num n1;
    n1 = func(5);
    num n2;
    n2 = func(6, y = 3);
\end{verbatim}

在上面的示例中，函数表达式func(5)只给出了必需参数x，
而函数表达式func(6, y = 3)则同时给出了可选参数y。

参数表达式中不能包含函数调用。

\section{聚合表达式}

聚合表达式实质上是由中括号包括的表达式列表，其给出了一组复合值，
可以用来给数组或者记录赋值。

\vspace{2ex}

\textbf{语法}
\begin{verbatim}
    aggregate_expr = '{' expr_list '}'
\end{verbatim}

当聚合表达式用来给数组赋值时，表达式中的各项必须有相同的类型。
而聚合表达式用来给记录赋值时，表达式中的各项需要一一对应于记录中的各数据成员的类型。
聚合表达式可以嵌套，即聚合表达式中的子表达式也可以为聚合表达式。

聚合表达式的类型取决于被赋值的数组或记录的类型，其值则是一个整体，包含了其所有的项。

例如：

\begin{verbatim}
    num arr[3] = {1, 2, 3};
    pos p1 = {1, 2, 3};
    string sarr[2] = {"hello", "world"};
    pose pose1 = {{1, 2, 3}, {1, 0, 0, 0}}; // 嵌套的聚合表达式
\end{verbatim}

在上面的示例中，同样的聚合表达式{1, 2, 3}，被赋值给arr时，被处理为数组类型；
而被赋值给p1时则被处理为pos类型，并且两个聚合表达式的值并不相同。
